package com.tenbent.product.base.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.tenbent.product.base.bo.BizObject;
import com.tenbent.product.base.convert.ObjectTypeConvert;
import com.tenbent.product.base.dto.change.ChangeBoToDto;
import com.tenbent.product.base.enums.error.ErrorEnum;
import com.tenbent.product.base.exception.TenbentException;
import com.tenbent.product.base.mapper.BaseSqlMapper;
import com.tenbent.product.base.query.page.PageResult;
import com.tenbent.product.base.query.page.Pageable;
import com.tenbent.product.base.service.BaseService;
import org.apache.commons.collections.map.HashedMap;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.*;

/**
 * 数据访问层基类实现
 *
 * @author Randy
 *         <p>
 *         Created by ThinkPad on 2017/7/27.
 */
@Service
public class BaseServiceImpl<T> implements BaseService<T> {

	private static Logger logger = LoggerFactory.getLogger(BaseServiceImpl.class);

	private Class<? extends BizObject> clazz;

	@Autowired
	@Qualifier("sqlSession")
	protected SqlSession sqlSession;

	@Autowired
	protected BaseSqlMapper<T> baseSqlMapper;

	@Autowired
	@Qualifier("changeBoToDto")
	protected ChangeBoToDto change;

	@PostConstruct
	public void init() {
		logger.info(" the base service is init !");
	}

	@PreDestroy
	public void destory() {
		logger.info(" the base service is destory !");
	}

	@Override
	public void add(T entity) throws TenbentException {
		try {
			((BizObject) entity).setDelFlag(0);
			((BizObject) entity).setCreateDate(new Date());
			((BizObject) entity).setModifyDate(new Date());
			baseSqlMapper.add(entity);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("add mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	@Override
	public void addContainPk(T entity) throws TenbentException {
		try {
			baseSqlMapper.addContainPk(entity);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("addContainPk mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	@Override
	public void update(T entity) throws TenbentException {
		try {
			((BizObject) entity).setModifyDate(new Date());
			baseSqlMapper.update(entity);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("update mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	@Override
	public void delete(T entity) throws TenbentException {
		delete(entity, false);
	}

	@Override
	public void delete(Integer id, Integer modiferId) throws TenbentException {
		delete(id, modiferId, false);
	}

	@Override
	@Transactional
	public void deleteByIds(Integer[] ids, Integer modiferId) throws TenbentException {
		deleteByIds(ids, modiferId, false);
	}

	@Override
	public void deleteLogic(T entity) throws TenbentException {
		delete(entity, true);
	}

	@Override
	public void deleteLogic(Integer id, Integer modiferId) throws TenbentException {
		delete(id, modiferId, true);
	}

	@Override
	@Transactional
	public void deleteLogicByIds(Integer[] ids, Integer modiferId) throws TenbentException {
		deleteByIds(ids, modiferId, true);
	}

	@Override
	public T getByKey(T entity) throws TenbentException {
		try {
			// 由于mybatis暂时无法对mapper中的select返回属性进行封装，暂时用下面的方式代替(没办法的办法)
			Map<String, Object> map = baseSqlMapper.getByKey(entity);
			return (T) ObjectTypeConvert.formMapToBO(map, entity.getClass());
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("getByKey mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	@Override
	public T getByKey(Integer key) throws TenbentException {
		try {
			BizObject obj = (BizObject) Class.forName(clazz.getName()).newInstance();
			obj.setId(key);
			return this.getByKey((T) obj);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("getByKey mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}

	}

	@Override
	public List<T> getAll() throws TenbentException {
		try {
			// 转对象
			List<Map<String, Object>> list = baseSqlMapper.getAll(clazz);
			return (List<T>) ObjectTypeConvert.fromListMapToListObject(list, clazz);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("getAll mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	@Override
	public List<T> queryObj(T entity) throws TenbentException {

		try {
			checkQueryParamForQuery(entity);
			List<Map<String, Object>> list = baseSqlMapper.queryByObj(entity);
			return (List<T>) ObjectTypeConvert.fromListMapToListObject(list, entity.getClass()); // 转对象
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("queryObj mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	@Override
	public List<Map<String, Object>> queryObjToMap(T entity) throws TenbentException {
		try {
			// return baseSqlMapper.queryByObj(entity);
			List<T> list = this.queryObj(entity);
			return ObjectTypeConvert.fromListObjToListMap((List<Object>) list);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("queryObjToMap mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	@Override
	public PageResult<T> queryPageObj(Pageable pageable) throws TenbentException {

		checkQueryParamForQuery(pageable);
		PageHelper.startPage(pageable.getPageNumber(), pageable.getPageSize());
		Page<Map<String, Object>> page = (Page<Map<String, Object>>) baseSqlMapper
				.queryByObj((T) pageable.getParamBizObject());
		PageResult<T> result = new PageResult<>(page.getTotal(), pageable);
		if (page != null && !page.isEmpty()) {
			coverPageForMapToList(page, result);
		}
		return result;
	}

	@Override
	public PageResult<Map<String, Object>> queryPageObjToMap(Pageable pageable) throws TenbentException {

		PageResult<T> page = queryPageObj(pageable);
		PageResult<Map<String, Object>> result = new PageResult<>(page.getTotal(), pageable);
		if (page != null && !page.isEmpty()) {
			coverPageForListToMap(page, result);
		}
		return result;
	}

	@Override
	public List<T> queryMap(Map<String, Object> paramMap) throws TenbentException {
		try {
			if (paramMap == null) {
				paramMap = new HashedMap();
			}
			checkMapAttributeInBizObjForQuery(paramMap);
			paramMap.put(BaseSqlMapper.TABLE_NAME, clazz.getName());
			List<Map<String, Object>> list = baseSqlMapper.queryByMap(paramMap);
			return (List<T>) ObjectTypeConvert.fromListMapToListObject(list, clazz);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("queryMap mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	@Override
	public List<Map<String, Object>> queryMapToMap(Map<String, Object> paramMap) throws TenbentException {

		try {
			List<T> list = this.queryMap(paramMap);
			return ObjectTypeConvert.fromListObjToListMap((List<Object>) list);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("queryMapToMap mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	@Override
	public PageResult<T> queryPageMap(Pageable pageable) throws TenbentException {

		Map<String, Object> paramMap = pageable.getParamMap();
		if (paramMap == null) {
			paramMap = new HashedMap();
		}
		checkMapAttributeInBizObjForQuery(paramMap);
		paramMap.put(BaseSqlMapper.TABLE_NAME, clazz.getName().toString());
		PageHelper.startPage(pageable.getPageNumber(), pageable.getPageSize());
		Page<Map<String, Object>> page = (Page<Map<String, Object>>) baseSqlMapper.queryByMap(paramMap);
		PageResult<T> result = new PageResult<>(page.getTotal(), pageable);
		if (page != null && !page.isEmpty()) {
			coverPageForMapToList(page, result);
		}
		return result;
	}

	@Override
	public PageResult<Map<String, Object>> queryPageMapToMap(Pageable pageable) throws TenbentException {
		PageResult<T> page = this.queryPageMap(pageable);
		PageResult<Map<String, Object>> result = new PageResult<>(page.getTotal(), pageable);
		if (page != null && !page.isEmpty()) {
			coverPageForListToMap(page, result);
		}
		return result;
	}

	@Override
	public T queryUniqueObj(T entity) throws TenbentException {
		List<T> list = this.queryObj(entity);
		return list != null && !list.isEmpty() ? list.get(0) : null;
	}

	@Override
	public T queryUniqueMap(Map<String, Object> paramMap) throws TenbentException {
		List<T> list = this.queryMap(paramMap);
		return list != null && !list.isEmpty() ? list.get(0) : null;
	}

	@Override
	public Map<String, Object> queryUniqueObjToMap(T entity) throws TenbentException {
		try {
			return ObjectTypeConvert.fromPropExpandToMap(this.queryUniqueObj(entity));
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("queryUniqueObjToMap mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	@Override
	public Map<String, Object> queryUniqueMapToMap(Map<String, Object> paramMap) throws TenbentException {
		try {
			return ObjectTypeConvert.fromPropExpandToMap(this.queryUniqueMap(paramMap));
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("queryUniqueObjToMap mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	public void setClazz(Class<? extends BizObject> clazz) {
		this.clazz = clazz;
	}

	/**
	 * 转换List<Map> 为 List<BizObject>
	 *
	 */
	private void coverPageForMapToList(Page<Map<String, Object>> page, PageResult<T> result) throws TenbentException {
		try {
			Page<T> covertPage = new Page();
			covertPage.addAll((List<T>) ObjectTypeConvert.fromListMapToListObject(page.getResult(), clazz));
			result.addAll(covertPage);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	/**
	 * 转换List<BizObject> 为 List<Map>
	 *
	 */
	private void coverPageForListToMap(PageResult<T> page, PageResult<Map<String, Object>> result)
			throws TenbentException {
		try {
			Page<Map<String, Object>> covertPage = new Page();
			covertPage.addAll(ObjectTypeConvert.fromListObjToListMap((List<Object>) page));
			result.addAll(covertPage);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	/**
	 * 检查查询分页对象参数
	 */
	private void checkBizObjParamForQueryPage(Pageable pageable) {
		if (pageable.getParamBizObject() == null) {
			throw new TenbentException(ErrorEnum.SERVICE.getValue(),
					"the query page param of extends BizObject is null , please check the param!");
		}
	}

	/**
	 * 检查查询分页对象参数
	 */
	private void checkQueryParamForQuery(Object obj) {
		if (obj == null) {
			throw new TenbentException(ErrorEnum.SERVICE.getValue(),
					"the query param of extends BizObject is null , please check the param!");
		}
		if (obj instanceof Pageable) {
			checkBizObjParamForQueryPage((Pageable) obj);
		}
	}

	/**
	 * 检查map查询参数是否与对象属性对应(必须全匹配即map中的属性必须与对象属性一直，否则报错)
	 */
	private void checkMapAttributeInBizObjForQuery(Map<String, Object> queryMap) {

		if (queryMap != null && !queryMap.isEmpty()) {
			if (!ObjectTypeConvert.isArrayStringAttrMatchsBoAttr(queryMap.keySet(), clazz)) {
				throw new TenbentException(ErrorEnum.SERVICE.getValue(),
						"the query map param key is not matchs attribute (bizObject)  , please check the param!");
			}
		}
	}

	/**
	 * 处理批量删除
	 *
	 * @param entity
	 *            要删除实体对象
	 *
	 * @param logicFlag
	 *            是否逻辑删除
	 */
	private void delete(T entity, boolean logicFlag) {
		try {
			if (!logicFlag) {
				baseSqlMapper.delete(entity);
			} else {
				baseSqlMapper.deleteLogic(entity);
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("delete mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	/**
	 * 处理批量删除
	 *
	 * @param id
	 *            要删除实体对象ID
	 *
	 * @param modiferId
	 *            修改人ID
	 *
	 * @param logicFlag
	 *            是否逻辑删除
	 */
	private void delete(Integer id, Integer modiferId, boolean logicFlag) {
		try {
			BizObject obj = clazz.newInstance();
			obj.setId(id);
			obj.setModifyId(modiferId);
			delete((T) obj, logicFlag);
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("delete mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

	/**
	 * 处理批量删除
	 *
	 * @param ids
	 *            要删除记录ID
	 * 
	 * @param logicFlag
	 *            是否逻辑删除
	 */
	private void deleteByIds(Integer[] ids, Integer modiferId, boolean logicFlag) {
		try {
			for (Integer id : ids) {
				BizObject obj = clazz.newInstance();
				obj.setId(id);
				obj.setModifyId(modiferId);
				delete((T) obj, logicFlag);
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("deleteByIds mapper is error, reason : {}", e.getMessage());
			throw new TenbentException(ErrorEnum.SERVICE.getValue(), e.getMessage());
		}
	}

}
